package com.lalameow.researchstation.kube

import com.lalameow.researchstation.ResearchStation
import com.lalameow.researchstation.api.technology.CommandReward
import com.lalameow.researchstation.api.technology.FluidRequirement
import com.lalameow.researchstation.api.technology.FluidResource
import com.lalameow.researchstation.api.technology.ItemRequirement
import com.lalameow.researchstation.api.technology.ItemResource
import com.lalameow.researchstation.api.technology.ItemReward
import com.lalameow.researchstation.api.technology.SingleTechnologyCriterion
import com.lalameow.researchstation.api.technology.StageReward
import com.lalameow.researchstation.api.technology.StatResource
import com.lalameow.researchstation.api.technology.Technologies
import com.lalameow.researchstation.api.technology.Technology
import com.lalameow.researchstation.api.technology.TechnologyCategories
import com.lalameow.researchstation.api.technology.TechnologyCategory
import com.lalameow.researchstation.api.technology.TechnologyCriterion
import com.lalameow.researchstation.api.technology.TechnologyRegistries
import com.lalameow.researchstation.api.technology.TechnologyRequirement
import com.lalameow.researchstation.api.technology.TechnologyResource
import com.lalameow.researchstation.api.technology.TechnologyReward
import com.lalameow.researchstation.identifier
import com.lalameow.researchstation.translatable
import dev.architectury.fluid.FluidStack
import dev.latvian.mods.kubejs.fluid.FluidStackJS
import dev.latvian.mods.kubejs.item.ItemStackJS
import dev.latvian.mods.kubejs.item.ingredient.IngredientJS
import net.minecraft.item.ItemStack
import net.minecraft.stat.Stat
import net.minecraft.text.Text
import net.minecraft.util.Identifier
import net.minecraft.util.registry.Registry

interface Builder<T> {
    fun build(): T
}

class TechnologyBuilderJS(
    id: String
) : Builder<Technology> {
    private var id = ResearchStation.identifier(id)!!
    private var name: Text = ResearchStation.translatable("technology.$id")
    private var description: Text? = null
    private var icon = mutableSetOf<ItemStack>()
    private var requirements = mutableSetOf<TechnologyRequirement<*>>()
    private var rewards = mutableSetOf<TechnologyReward>()
    private var criteria = mutableSetOf<TechnologyCriterion>()
    private var visible = false

    fun name(name: String) = apply { this.name = Text.of(name) }
    fun description(description: String?) = apply { this.description = description?.let(Text::of) }
    fun icon(icon: IngredientJS) = apply { this.icon += icon.stacks.map { it.itemStack } }
    fun requirement(requirement: Any) =
        apply {
            when (requirement) {
                is Builder<*> -> requirements += (requirement as Builder<TechnologyRequirement<*>>).build()
                is ItemStackJS -> requirements += TechnologyRequirements.ITEM().item(requirement).build()
                is FluidStackJS -> requirements += TechnologyRequirements.FLUID_STACK().fluid(requirement).build()
            }
        }

    fun reward(reward: Builder<out TechnologyReward>) = apply { rewards += reward.build() }
    fun criterion(criterion: Builder<out TechnologyCriterion>) = apply { criteria += criterion.build() }
    fun visible(visible: Boolean) = apply { this.visible = visible }
    override fun build() = Technology(id, icon, requirements, rewards, criteria, visible, name, description).also {
        Technologies += it
    }
}

class TechnologyCategoryBuilderJS(
    id: String
) : Builder<TechnologyCategory> {
    private var id = ResearchStation.identifier(id)!!
    private var name: Text = ResearchStation.translatable("technology.$id")
    private var description: Text? = null
    private var icon = mutableSetOf<ItemStack>()
    private var technologies = mutableSetOf<Technology>()
    private var background: Identifier? = null

    fun name(name: String) = apply { this.name = Text.of(name) }
    fun description(description: String?) = apply { this.description = description?.let(Text::of) }
    fun icon(icon: IngredientJS) = apply { this.icon += icon.stacks.map { it.itemStack } }
    fun technology(technology: TechnologyBuilderJS) = apply { technologies += technology.build() }
    fun background(background: Identifier?) = apply { this.background = background }
    override fun build() = TechnologyCategory(id, icon, technologies, background, name, description).also {
        TechnologyCategories += it
    }
}

object TechnologyBuildersJS {
    /**
     * 资源
     */
    object Resource {
        class Item : Builder<ItemResource> {
            private var item: ItemStack? = null

            fun item(item: ItemStackJS) = apply { this.item = item.itemStack }

            override fun build() = ItemResource(item!!)
        }

        class Fluid : Builder<FluidResource> {
            private var fluid: FluidStack? = null

            fun fluid(fluid: FluidStackJS) = apply { this.fluid = fluid.fluidStack }

            override fun build() = FluidResource(fluid!!)
        }

        class PlayerStat : Builder<StatResource> {
            private var stat: Stat<*>? = null
            private var name: Text? = null
            private var value: Long? = null

            // TODO 实现 Stat KubeJS 接口
            fun value(value: Long) = apply { this.value = value }

            override fun build() = StatResource(stat!!, value!!)
        }
    }

    /**
     * 奖励
     */
    object Reward {
        class Item : Builder<ItemReward> {
            private var items = mutableListOf<ItemStack>()
            private var name: Text? = null

            fun name(name: String) = apply { this.name = Text.of(name) }

            fun item(item: IngredientJS) = apply { this.items += item.stacks.map { it.itemStack } }

            override fun build() = ItemReward(
                items.takeIf { it.isNotEmpty() }!!,
                name ?: ResearchStation.translatable("rewards.item", items)
            )
        }

        class Command : Builder<CommandReward> {
            private var commands = mutableListOf<String>()
            private var name: Text? = null
            fun name(name: String) = apply { this.name = Text.of(name) }

            fun command(command: String) = apply { this.commands += command }

            override fun build() = CommandReward(
                commands.takeIf { it.isNotEmpty() }!!,
                name ?: ResearchStation.translatable("rewards.command", commands)
            )
        }

        class Stage : Builder<StageReward> {
            private var stages = mutableSetOf<String>()
            private var name: Text? = null

            fun name(name: String) = apply { this.name = Text.of(name) }

            fun stage(stage: String) = apply { this.stages += stage }

            override fun build() = StageReward(
                stages.takeIf { it.isNotEmpty() }!!,
                name ?: ResearchStation.translatable("rewards.stage", stages)
            )
        }
    }

    /**
     * 需求
     */
    object Requirement {
        class Item : Builder<ItemRequirement> {
            private var item: ItemStack? = null
            fun item(item: ItemStackJS) = apply { this.item = item.itemStack }
            override fun build() = ItemRequirement(ItemResource(item!!))
        }

        class Fluid : Builder<FluidRequirement> {
            private var fluid: FluidStack? = null
            fun fluid(fluid: FluidStackJS) = apply { this.fluid = fluid.fluidStack }
            override fun build() = FluidRequirement(FluidResource(fluid!!))
        }
        // TODO Stat 需求
    }

    /**
     * 前置
     */
    object Criterion {
        class SingleTechnology : Builder<SingleTechnologyCriterion> {
            private var technology: Technology? = null
            private var name: Text? = null

            fun name(name: String) = apply { this.name = Text.of(name) }

            fun technology(technology: String) =
                apply { this.technology = Technologies[ResearchStation.identifier(technology)!!]!! }

            override fun build() = SingleTechnologyCriterion(technology!!, name ?: technology!!.name)
        }
    }
}

object TechnologyCriteria {
    val TECHNOLOGY = register("technology") { TechnologyBuildersJS.Criterion.SingleTechnology() }

    private fun <T : () -> Builder<out TechnologyCriterion>> register(id: String, resource: T): T =
        Registry.register(TechnologyRegistries.TECHNOLOGY_CRITERION, ResearchStation.identifier(id), resource)
}

object TechnologyRequirements {
    val ITEM = register("item") { TechnologyBuildersJS.Requirement.Item() }
    val FLUID_STACK = register("fluid_stack") { TechnologyBuildersJS.Requirement.Fluid() }

    private fun <T : () -> Builder<out TechnologyRequirement<*>>> register(id: String, resource: T): T =
        Registry.register(TechnologyRegistries.TECHNOLOGY_REQUIREMENT, ResearchStation.identifier(id), resource)
}

object TechnologyResources {
    val ITEM = register("item") { TechnologyBuildersJS.Resource.Item() }
    val FLUID_STACK = register("fluid_stack") { TechnologyBuildersJS.Resource.Fluid() }
    val PLAYER_STAT = register("player_stat") { TechnologyBuildersJS.Resource.PlayerStat() }

    private fun <V, T : () -> Builder<out TechnologyResource<V>>> register(id: String, resource: T): T =
        Registry.register(TechnologyRegistries.TECHNOLOGY_RESOURCE, ResearchStation.identifier(id), resource)
}

object TechnologyRewards {
    val ITEM = register("item") { TechnologyBuildersJS.Reward.Item() }

    val COMMAND = register("command") { TechnologyBuildersJS.Reward.Command() }

    val STAGE = register("stage") { TechnologyBuildersJS.Reward.Stage() }

    private fun <T : () -> Builder<out TechnologyReward>> register(id: String, reward: T): T =
        Registry.register(TechnologyRegistries.TECHNOLOGY_REWARD, ResearchStation.identifier(id), reward)
}
